package r3.spring;

import org.springframework.beans.PropertyValue;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.config.Scope;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element;
import r3.common.R3Utils;
import r3.spring.bean.ApplicationBean;
import r3.spring.bean.LeaderBean;
import r3.spring.bean.RegistryBean;
import r3.spring.bean.WorkerBean;

/**
 * R3BeanDefinitonParser
 *
 * @author zhoufn
 * @create 2017-12-25 10:19
 **/
public class R3BeanDefinitonParser extends AbstractSimpleBeanDefinitionParser {

    private Class<?> clazz;

    public R3BeanDefinitonParser(Class<?> clazz) {
        this.clazz = clazz;
    }

    /**
     * Parse the supplied {@link Element} and populate the supplied
     * {@link BeanDefinitionBuilder} as required.
     * <p>This implementation maps any attributes present on the
     * supplied element to {@link PropertyValue}
     * instances, and
     * {@link BeanDefinitionBuilder#addPropertyValue(String, Object) adds them}
     * to the
     * {@link BeanDefinition builder}.
     * <p>The {@link #extractPropertyName(String)} method is used to
     * reconcile the name of an attribute with the name of a JavaBean
     * property.
     *
     * @param element       the XML element being parsed
     * @param parserContext
     * @param builder       used to define the {@code BeanDefinition}  @see #extractPropertyName(String)
     */
    protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
        builder.getBeanDefinition().setBeanClass(this.clazz);
        if(this.clazz.equals(ApplicationBean.class)){
            String id = element.getAttribute("id");
            String name = element.getAttribute("name");
            String host = element.getAttribute("host");
            if("auto".equalsIgnoreCase(host)){
                host = R3Utils.getNetIP();
            }
            int port = Integer.parseInt(element.getAttribute("port"));
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("id",id);
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("name",name);
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("host",host);
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("port",port);
        }else if(this.clazz.equals(RegistryBean.class)){
            String address = element.getAttribute("address");
            String namespace = element.getAttribute("namespace");
            String sessionTimeout = element.getAttribute("sessionTimeout");
            String connectionTimeout = element.getAttribute("connectionTimeout");
            String interval = element.getAttribute("interval");
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("address",address);
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("namespace",namespace);
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("interval",Integer.parseInt(interval));
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("sessionTimeout",Integer.parseInt(sessionTimeout));
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("connectionTimeout",Integer.parseInt(connectionTimeout));
        }else if(this.clazz.equals(WorkerBean.class)){
            String id = element.getAttribute("id");
            String ref = element.getAttribute("ref");
            String interfce = element.getAttribute("interface");
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("id",id);
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("ref",new RuntimeBeanReference(ref));
            builder.getBeanDefinition().getPropertyValues().addPropertyValue("interfce",interfce);
        }else if(this.clazz.equals(LeaderBean.class)){
            String id = element.getAttribute("id");
            String interfce = element.getAttribute("interface");
            String loadBalance = element.getAttribute("loadbalance");
            builder.getBeanDefinition().setFactoryMethodName("createLeader");
            ConstructorArgumentValues args = new ConstructorArgumentValues();
            args.addGenericArgumentValue(interfce);
            args.addGenericArgumentValue(loadBalance);
            builder.getBeanDefinition().setConstructorArgumentValues(args);
        }
    }

    /**
     * Determine the bean class corresponding to the supplied {@link Element}.
     * <p>Note that, for application classes, it is generally preferable to
     * override {@link #getBeanClassName} instead, in order to avoid a direct
     * dependence on the bean implementation class. The BeanDefinitionParser
     * and its NamespaceHandler can be used within an IDE plugin then, even
     * if the application classes are not available on the plugin's classpath.
     *
     * @param element the {@code Element} that is being parsed
     * @return the {@link Class} of the bean that is being defined via parsing
     * the supplied {@code Element}, or {@code null} if none
     * @see #getBeanClassName
     */
    protected Class<?> getBeanClass(Element element) {
        if(this.clazz.equals(LeaderBean.class)){
            try {
                return Class.forName(element.getAttribute("interface"));
            } catch (ClassNotFoundException e) {
                throw new RuntimeException();
            }
        }
        return this.clazz;
    }
}
